/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <thrift/lib/cpp/EventHandlerBase.h>
#include <algorithm>

using std::remove;
using std::shared_ptr;
using std::vector;

using RWMutex = folly::SharedMutex;
using RLock = RWMutex::ReadHolder;
using WLock = RWMutex::WriteHolder;

namespace apache {
namespace thrift {

TProcessorBase::TProcessorBase() {
  RLock lock{getRWMutex()};
  for (auto factory : getFactories()) {
    auto handler = factory->getEventHandler();
    if (handler) {
      addEventHandler(handler);
    }
  }
}

void TProcessorBase::addProcessorEventHandlerFactory(
    std::shared_ptr<TProcessorEventHandlerFactory> factory) {
  WLock lock{getRWMutex()};
  assert(
      find(getFactories().begin(), getFactories().end(), factory) ==
      getFactories().end());
  getFactories().push_back(factory);
}

void TProcessorBase::removeProcessorEventHandlerFactory(
    std::shared_ptr<TProcessorEventHandlerFactory> factory) {
  WLock lock{getRWMutex()};
  assert(
      find(getFactories().begin(), getFactories().end(), factory) !=
      getFactories().end());
  getFactories().erase(
      remove(getFactories().begin(), getFactories().end(), factory),
      getFactories().end());
}

RWMutex& TProcessorBase::getRWMutex() {
  static auto* mutex = new RWMutex{};
  return *mutex;
}

vector<shared_ptr<TProcessorEventHandlerFactory>>&
TProcessorBase::getFactories() {
  static vector<shared_ptr<TProcessorEventHandlerFactory>>* factories =
      new vector<shared_ptr<TProcessorEventHandlerFactory>>();
  return *factories;
}

TClientBase::TClientBase() {
  // Automatically ask all registered factories to produce an event
  // handler, and attach the handlers
  RLock lock{getRWMutex()};
  for (auto factory : getFactories()) {
    auto handler = factory->getEventHandler();
    if (handler) {
      addEventHandler(handler);
    }
  }
}

void TClientBase::addClientEventHandlerFactory(
    std::shared_ptr<TProcessorEventHandlerFactory> factory) {
  WLock lock{getRWMutex()};
  assert(
      find(getFactories().begin(), getFactories().end(), factory) ==
      getFactories().end());
  getFactories().push_back(factory);
}

void TClientBase::removeClientEventHandlerFactory(
    std::shared_ptr<TProcessorEventHandlerFactory> factory) {
  WLock lock{getRWMutex()};
  assert(
      find(getFactories().begin(), getFactories().end(), factory) !=
      getFactories().end());
  getFactories().erase(
      remove(getFactories().begin(), getFactories().end(), factory),
      getFactories().end());
}

RWMutex& TClientBase::getRWMutex() {
  static auto* mutex = new RWMutex{};
  return *mutex;
}
vector<shared_ptr<TProcessorEventHandlerFactory>>& TClientBase::getFactories() {
  static vector<shared_ptr<TProcessorEventHandlerFactory>>* factories =
      new vector<shared_ptr<TProcessorEventHandlerFactory>>();
  return *factories;
}

} // namespace thrift
} // namespace apache
